/*
 * OREKIT-X
 * Copyright 2002-2008 CS Communication & Systemes
 * 
 * Parts of this software package have been licensed to CS
 * Communication & Systemes (CS) under one or more contributor license
 * agreements.  See the NOTICE file distributed with this work for
 * additional information.
 *  
 * This is an experimental copy of OREKIT from www.orekit.org.
 * Please use the original OREKIT from orekit.org for normal work
 * unrelated to this research project.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License.  You
 * may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied.  See the License for the specific language governing
 * permissions and limitations under the License.
 */
package ore.frames;

import ore.Transform;
import ore.errors.OrekitException;
import ore.time.AbsoluteDate;
import ore.time.DateComponents;
import ore.time.TimeComponents;
import ore.time.TimeScalesFactory;

import org.apache.commons.math.geometry.Rotation;
import org.apache.commons.math.geometry.Vector3D;

/** 
 * Terrestrial Intermediate Reference Frame 2000, child frame of
 * {@link CIRF2000Frame}.
 * 
 * Pole motion is not considered : Pseudo Earth Fixed Frame. It handles
 * the earth rotation angle
 * 
 */
public class TIRF2000Frame
    extends ore.Frame
{
    /** 
     * Reference date of Capitaine's Earth Rotation Angle model.
     */
    private static final AbsoluteDate ERA_REFERENCE =
        new AbsoluteDate(DateComponents.J2000_EPOCH, TimeComponents.H12, TimeScalesFactory.getTAI());
    /**
     * Constant term of Capitaine's Earth Rotation Angle model.
     */
    private static final double ERA_0 = TWO_PI * 0.7790572732640;
    /** 
     * Rate term of Capitaine's Earth Rotation Angle model.  (radians
     * per day, main part)
     */
    private static final double ERA_1A = TWO_PI;
    /** 
     * Rate term of Capitaine's Earth Rotation Angle model.  (radians
     * per day, fractional part)
     */
    private static final double ERA_1B = ERA_1A * 0.00273781191135448;
    /**
     * Component of transform from parent CIRF
     */
    private final static Vector3D RotationRate = new Vector3D(((ERA_1A + ERA_1B) / SECONDS_PER_DAY), Vector3D.PLUS_K);
    /** 
     * Cached date to avoid useless calculus.
     */
    private AbsoluteDate cachedDate;
    /** 
     * Earth Rotation Angle, in radians.
     */
    private double era;
    /** 
     * Tidal correction (null if tidal effects are ignored).
     */
    public final TidalCorrection tidalCorrection;
    /** 
     * EOP history.
     */
    public final EOP2000History eopHistory;


    /** 
     * Constructor ignoring tidal effects.
     * 
     * @param date the current date
     * @param name the string representation
     * 
     * @exception OrekitException Nutation cannot be computed
     */
    protected TIRF2000Frame(AbsoluteDate date, String name)
        throws OrekitException
    {
        this(true, date, name);
    }

    /** 
     * @param ignoreTidalEffects Tidal effects shall be ignored
     * @param date Current date
     * @param name Frame name string
     * 
     * @exception OrekitException if nutation cannot be computed
     */
    protected TIRF2000Frame(boolean ignoreTidalEffects, AbsoluteDate date, String name)
        throws OrekitException
    {
        super(FramesFactory.getCIRF2000(), null, name);
        if (ignoreTidalEffects)
            this.tidalCorrection = null;
        else
            this.tidalCorrection = new TidalCorrection();

        this.eopHistory = new EOP2000History();
        /*
         * Synchronize the frame
         */
        this.updateFrame(date);
    }


    /** 
     * @param date Date at which the correction is desired
     * 
     * @return Pole correction including both EOP values and tidal
     * correction as configured
     */
    public PoleCorrection getPoleCorrection(AbsoluteDate date) {
        PoleCorrection eop = eopHistory.getPoleCorrection(date);
        if (tidalCorrection == null)
            return eop;
        else {
            final PoleCorrection tidal = tidalCorrection.getPoleCorrection(date);
            return new PoleCorrection((eop.getXp() + tidal.getXp()),
                                      (eop.getYp() + tidal.getYp()));
        }
    }
    /** 
     * @param date Current date
     * 
     * @return Earth Rotation Angle at the current date in radians
     * 
     * @exception OrekitException if nutation model cannot be computed
     */
    public double getEarthRotationAngle(AbsoluteDate date) throws OrekitException {
        this.updateFrame(date);
        return this.era;
    }
    /** 
     * Update the frame to the given date.  The update considers the
     * earth rotation from IERS data.
     * 
     * @param date New value of the date
     * 
     * @exception OrekitException The nutation model data embedded in
     * the library cannot be read
     */
    protected void updateFrame(final AbsoluteDate date)
        throws OrekitException
    {
        if ((this.cachedDate == null) || !this.cachedDate.equals(date)) {
            /*
             * Compute Earth Rotation Angle using Nicole Capitaine model (2000)
             */
            final double tidalDtu1   = (tidalCorrection == null) ? 0 : tidalCorrection.getDUT1(date);
            final double dtu1        = eopHistory.getUT1MinusUTC(date);
            final double utcMinusTai = TimeScalesFactory.getUTC().offsetFromTAI(date);
            final double tu =
                (date.durationFrom(ERA_REFERENCE) + utcMinusTai + dtu1 + tidalDtu1) / 86400.0;

            this.era  = (ERA_0 + (ERA_1A * tu) + (ERA_1B * tu));
            this.era -= (TWO_PI * Math.floor((this.era + Math.PI) / TWO_PI));
            /*
             * Transform from parent CIRF
             */
            this.setTransform(new Transform(new Rotation(Vector3D.PLUS_K, -era), RotationRate));
            this.cachedDate = date;
        }
    }
}
